#include "usb.h"

#include "h01.h"
#include "iap.h"

static ubyte	rxLen=0;
static ushort	rxBuf[32];
static ubyte	usbOk=0;

void dispatchR(ushort *rx){
	switch(*rx){
	case RPT_FIRMWARE:
		iap(rx);
		break;
	case RPT_STEPREC:
		break;
	}
}
void dispatchS(ushort *rx){
	uint t=0;
	for(ushort *e=rx+32,*p=rx;p<e;){
		ushort i=*p++,v=*p++;
		switch(i){
		case RPT_TICKLO:
			t=(t&0xffff0000)|v;
			break;
		case RPT_TICKHI:
			t=(t&0xffff)|(v<<16);
			break;
		}
	}
	if(t){
		t-=RTC_GetCounter();
		int d=t-savRam.timeOfs;
		if(d<0)
			d=-d;
		if(d>5){
			BKP_WriteBackupRegister(BACKUP_TMOFSL,t);
			BKP_WriteBackupRegister(BACKUP_TMOFSH,t>>16);
			savRam.timeOfs=t;
			FLASH_Unlock();
			flashErasePage(&savRom);
			flashCpy(&savRom,&savRam,sizeof(SAVE));
			FLASH_Lock();
		}
	}
}
static void int2Unicode(uint value,ubyte *pbuf,ubyte len){
	// Convert Hex 32Bits value into char.
	ubyte idx=0;
	for(idx=0;idx<len;idx++){
		if(((value>>28))<0xA)
			pbuf[2*idx]=(value>>28)+'0';
		else pbuf[2*idx]=(value>>28)+'A'-10;
		value=value<<4;
		pbuf[2*idx+1]=0;
	}
}
static void interruptsConfig(void){
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel=USB_LP_CAN1_RX0_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_Init(&NVIC_InitStructure);
}

//------------------------------------------------------------------------------

void EP1_OUT_Callback(void){
	rxLen=GetEPRxCount(ENDP1);
	PMAToUserBufferCopy((ubyte*)rxBuf,ENDP1_RXADDR,rxLen);
	SetEPRxStatus(ENDP1,EP_RX_VALID);
}

//------------------------------------------------------------------------------

void cableConfig(FunctionalState NewState){
	// Software Connection/Disconnection of USB Cable.
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	
	GPIO_InitTypeDef GPIO_USBpp;
	GPIO_USBpp.GPIO_Pin=GPIO_Pin_15;
	GPIO_USBpp.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_USBpp.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_USBpp);
	
	if(NewState !=DISABLE)
		GPIO_SetBits(GPIOB,GPIO_Pin_15);
	else GPIO_ResetBits(GPIOB,GPIO_Pin_15);
}
void dispatch(void){
	if(rxLen){
		IWDG_ReloadCounter();	// feed dog
		if(*rxBuf&0x8000)
			dispatchR(rxBuf);
		else dispatchS(rxBuf);
		rxLen=0;
	}
}
void enterLoPower(void){
	// Set the device state to suspend
	// Power-off system clocks and power while entering suspend mode.
	bDeviceState=SUSPENDED;
	//usbplug=0;
}
void leaveLoPower(void){
	// Restores system clocks and power while exiting suspend mode.
	DEVICE_INFO *pInfo=&Device_Info;
	// Set the device state to the correct state
	if(pInfo->Current_Configuration!=0){
		// Device configured
		bDeviceState=CONFIGURED;
		//usbplug=1;
	}else bDeviceState=ATTACHED;
}
uint sendReport(void *report){
	if(bDeviceState==CONFIGURED && usbWait()){
		USB_SIL_Write(EP2_IN,report,64);
		SetEPTxValid(ENDP2);	//Enable endpoint for transmission
		return 64;
	}
	return 0;
}
void serialNum(void){
	//Create the serial number string descriptor.
	uint s=*(uint*)0x1FFFF7E8;
	if(s){
		int2Unicode(s,&CustomHID_StringSerial[18],8);
		int2Unicode(*(uint*)0x1FFFF7EC,&CustomHID_StringSerial[34],8);
		int2Unicode(*(uint*)0x1FFFF7F0,&CustomHID_StringSerial[50],8);
	}
}
/*void serialNum0(void){
	//Create the serial number string descriptor.
	uint Device_Serial0,Device_Serial1,Device_Serial2;
	Device_Serial0=*(__IO uint*)(0x1FFFF7E8);
	Device_Serial1=*(__IO uint*)(0x1FFFF7EC);
	Device_Serial2=*(__IO uint*)(0x1FFFF7F0);
	Device_Serial0 +=Device_Serial2;	
	if(Device_Serial0!=0){
		int2Unicode(Device_Serial0,&CustomHID_StringSerial[2],8);
		int2Unicode(Device_Serial1,&CustomHID_StringSerial[18],8);
	}
}*/
uint usbBusy(void){
	return GetEPTxStatus(ENDP2)!=EP_TX_NAK;
}
void usbOff(void){
	if(usbOk){
		usbOk=0;
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB,DISABLE);
	}
}
void usbOn(void){
	if(!usbOk){
		RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_Div1);
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB,ENABLE);
		cableConfig(ENABLE);
		interruptsConfig();
		USB_Init();
		usbOk=1;
		iap(0);	// reset iap;
	}
}
ubyte usbNoPower(void){
	return GPIO_ReadInputDataBit(GPIOC,PIN_CHIN);
}
ubyte usbWait(void){
	for(uint i=100;i;i--){
		if(!usbBusy())
			return 1;
		__WFE();
	}
	return 0;
}